package org.mariadb.jdbc; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import javax.sql.XAConnection; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.UUID; import static org.junit.Assert.*; public class DistributedTransaction extends BaseTest { MariaDbDataSource dataSource; /** * Initialisation. */ public DistributedTransaction() { dataSource = new MariaDbDataSource(); dataSource.setServerName(hostname); dataSource.setPortNumber(port); dataSource.setDatabaseName(database); dataSource.setUser(username); dataSource.setPassword(password); } @BeforeClass() public static void initClass() throws SQLException { createTable("xatable", "i int", "ENGINE=InnoDB"); } @Before public void checkSupported() throws SQLException { requireMinimumVersion(5, 0); } Xid newXid() { return new MariaDbXid(1, UUID.randomUUID().toString().getBytes(), UUID.randomUUID().toString().getBytes()); } Xid newXid(Xid branchFrom) { return new MariaDbXid(1, branchFrom.getGlobalTransactionId(), UUID.randomUUID().toString().getBytes()); } /** * 2 phase commit , with either commit or rollback at the end. * * @param doCommit must commit * @throws Exception exception */ void test2PhaseCommit(boolean doCommit) throws Exception { int connectionNumber = 1; Xid parentXid = newXid(); Connection[] connections = new Connection[connectionNumber]; XAConnection[] xaConnections = new XAConnection[connectionNumber]; XAResource[] xaResources = new XAResource[connectionNumber]; Xid[] xids = new Xid[connectionNumber]; try { for (int i = 0; i < connectionNumber; i++) { xaConnections[i] = dataSource.getXAConnection(); connections[i] = xaConnections[i].getConnection(); xaResources[i] = xaConnections[i].getXAResource(); xids[i] = newXid(parentXid); } startAllResources(connectionNumber, xaResources, xids); insertDatas(connectionNumber, connections); endAllResources(connectionNumber, xaResources, xids); prepareAllResources(connectionNumber, xaResources, xids); for (int i = 0; i < connectionNumber; i++) { if (doCommit) { xaResources[i].commit(xids[i], false); } else { xaResources[i].rollback(xids[i]); } } // check the completion try (ResultSet rs = sharedConnection.createStatement().executeQuery("SELECT * from xatable order by i")) { if (doCommit) { for (int i = 0; i < connectionNumber; i++) { rs.next(); assertEquals(rs.getInt(1), i); } } else { assertFalse(rs.next()); } } } finally { for (int i = 0; i < connectionNumber; i++) { try { if (xaConnections[i] != null) { xaConnections[i].close(); } } catch (Exception e) { e.printStackTrace(); } } } } private void startAllResources(int connectionNumber, XAResource[] xaResources, Xid[] xids) throws XAException { for (int i = 0; i < connectionNumber; i++) { xaResources[i].start(xids[i], XAResource.TMNOFLAGS); } } private void endAllResources(int connectionNumber, XAResource[] xaResources, Xid[] xids) throws XAException { for (int i = 0; i < connectionNumber; i++) { xaResources[i].end(xids[i], XAResource.TMSUCCESS); } } private void prepareAllResources(int connectionNumber, XAResource[] xaResources, Xid[] xids) throws XAException { for (int i = 0; i < connectionNumber; i++) { xaResources[i].prepare(xids[i]); } } private void insertDatas(int connectionNumber, Connection[] connections) throws SQLException { for (int i = 0; i < connectionNumber; i++) { connections[i].createStatement().executeUpdate("INSERT INTO xatable VALUES (" + i + ")"); } } @Test public void testCommit() throws Exception { test2PhaseCommit(true); } @Test public void testRollback() throws Exception { test2PhaseCommit(false); } @Test public void testRecover() throws Exception { XAConnection xaConnection = dataSource.getXAConnection(); try { Connection connection = xaConnection.getConnection(); Xid xid = newXid(); XAResource xaResource = xaConnection.getXAResource(); xaResource.start(xid, XAResource.TMNOFLAGS); connection.createStatement().executeQuery("SELECT 1"); xaResource.end(xid, XAResource.TMSUCCESS); xaResource.prepare(xid); Xid[] recoveredXids = xaResource.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assertTrue(recoveredXids != null); assertTrue(recoveredXids.length > 0); boolean found = false; for (Xid x : recoveredXids) { if (x != null && x.equals(xid)) { found = true; break; } } assertTrue(found); } finally { xaConnection.close(); } } @Test public void resumeAndJoinTest() throws Exception { Connection conn1 = null; MariaDbDataSource ds = new MariaDbDataSource(); ds.setUrl(connU); ds.setDatabaseName(database); ds.setUser(username); ds.setPassword(password); ds.setPort(port); XAConnection xaConn1 = null; Xid xid = newXid(); try { xaConn1 = ds.getXAConnection(); XAResource xaRes1 = xaConn1.getXAResource(); conn1 = xaConn1.getConnection(); xaRes1.start(xid, XAResource.TMNOFLAGS); conn1.createStatement().executeQuery("SELECT 1"); xaRes1.end(xid, XAResource.TMSUCCESS); xaRes1.start(xid, XAResource.TMRESUME); conn1.createStatement().executeQuery("SELECT 1"); xaRes1.end(xid, XAResource.TMSUCCESS); xaRes1.commit(xid, true); xaConn1.close(); xaConn1 = ds.getXAConnection(); xaRes1 = xaConn1.getXAResource(); conn1 = xaConn1.getConnection(); xaRes1.start(xid, XAResource.TMNOFLAGS); conn1.createStatement().executeQuery("SELECT 1"); xaRes1.end(xid, XAResource.TMSUCCESS); try { xaRes1.start(xid, XAResource.TMJOIN); fail(); // without pinGlobalTxToPhysicalConnection=true } catch (XAException xaex) { if (xaConn1 != null) { xaConn1.close(); } } ds.setProperties("pinGlobalTxToPhysicalConnection=true"); xaConn1 = ds.getXAConnection(); xaRes1 = xaConn1.getXAResource(); conn1 = xaConn1.getConnection(); xaRes1.start(xid, XAResource.TMNOFLAGS); conn1.createStatement().executeQuery("SELECT 1"); xaRes1.end(xid, XAResource.TMSUCCESS); xaRes1.start(xid, XAResource.TMJOIN); conn1.createStatement().executeQuery("SELECT 1"); xaRes1.end(xid, XAResource.TMSUCCESS); xaRes1.commit(xid, true); } finally { if (xaConn1 != null) { xaConn1.close(); } } } }